<template>
  <el-card class="attr-card">
    <!--
      表单校验：
        el-form
          model form表单需要校验的数据
          rules 表单校验规则
          ref   获取form组件实例对象，从而调用上面的方法
            validate
        el-form-item
          prop 指定使用什么表单校验规则
        表单项组件。v-model收集数据
     -->
    <el-form ref="formRef" :model="attrForm" :rules="rules">
      <el-form-item label="属性名" label-width="70px" prop="attrName">
        <el-input
          v-model="attrForm.attrName"
          placeholder="请输入属性名"
          class="attr-input"
        />
      </el-form-item>

      <el-form-item prop="attrValueList">
        <!-- 看属性名是否有值 -->
        <el-button
          :disabled="!attrForm.attrName"
          type="primary"
          icon="el-icon-plus"
          class="attr-btn"
          @click="showAddAttrValue"
        >
          添加属性值
        </el-button>

        <el-table :data="attrForm.attrValueList" border class="attr-table">
          <el-table-column
            type="index"
            label="序号"
            width="50"
            align="center"
          />
          <el-table-column label="属性值名称">
            <template v-slot="{ row, $index }">
              <!-- 官方文档有时候存在一些问题，不能解决 autofocus -->
              <!-- <el-input placeholder="请输入属性值" size="small" autofocus /> -->
              <!--
                el-input 编辑模式
                  失去焦点要变成显示模式
                div 显示模式
                  点击div标签要变成编辑模式
                相互切换 -> v-if / v-show

                收集input输入的数据，作为显示模式显示内容

                el-input是组件，组件实现了blur事件，并没有实现keyup事件
                那么想要给组件绑定没有实现的事件：.native
                默认事件都是自定义事件，使用.native绑定的事件就是原声DOM事件

                为什么获取焦点只有最后一个input能生效？

                问题：每一行的el-input都会设置一个ref标识，ref="input"
                ref属性会讲这个DOM元素挂载到this.$refs.input, 后面会覆盖掉前面的
                解决：
                  1. 让ref标识每一行都不一样
                    直接使用下标作为ref的标识
                    通过作用域插槽得到$index
                  2. 将v-show改为v-if就ok
                    v-show 隐藏时DOM元素还在DOM树中，只是隐藏了
                      因为所有DOM元素都还在，同一个标识后面会覆盖掉前面的
                    v-if   元素会从DOM树移除，他不在了
                      DOM元素只会存在一个，所以获取到的就是你当前点击的
              -->
              <el-input
                v-if="row.isShowEdit"
                ref="input"
                v-model="row.valueName"
                placeholder="请输入属性值"
                size="small"
                @blur="addAttrValue(row, $index)"
                @keyup.enter.native="addAttrValue(row, $index)"
              />
              <!-- <div v-show="!row.isShowEdit" @click="showEdit(row)"> -->
              <div v-else @click="showEdit(row)">
                {{ row.valueName }}
              </div>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="100">
            <template v-slot="{ $index }">
              <!--
                element-ui  el-popconfirm组件的文档写的不对
                事件名实际叫做onConfirm
               -->
              <el-popconfirm
                title="确定删除吗？"
                icon="el-icon-info"
                icon-color="red"
                @onConfirm="delAttrValue($index)"
              >
                <el-tooltip
                  slot="reference"
                  effect="dark"
                  content="删除属性值"
                  placement="top"
                >
                  <el-button type="danger" size="small" icon="el-icon-delete" />
                </el-tooltip>
              </el-popconfirm>
            </template>
          </el-table-column>
        </el-table>
      </el-form-item>

      <el-form-item>
        <el-button class="attr-btn" type="primary" @click="addAttr">
          确定
        </el-button>
        <el-button @click="cancel">取消</el-button>
      </el-form-item>
    </el-form>
  </el-card>
</template>

<script>
import { mapState } from 'vuex'
import { addAttr } from '@/api/product/attr'

export default {
  name: 'AddOrUpdateAttr',
  data() {
    return {
      // attrForm: {
      //   attrName: '', // 属性名
      //   attrValueList: [] // 属性值列表
      // },
      /*
        不使用mapState,mapState要在计算属性中映射
        而Vue解析组件时先解析data在computed，所以data中读取数据是undefined
      */
      attrForm: this.$store.state.attr.attr, // 从vuex读取
      rules: {
        attrName: [
          { required: true, message: '请输入属性名', trigger: 'blur' }
        ],
        attrValueList: [
          {
            required: true,
            type: 'array', // 数组至少有一项值才能通过
            message: '请至少添加一个属性值',
            trigger: 'blur'
          }
        ]
      }
    }
  },
  computed: {
    ...mapState('category', ['category3Id'])
    // ...mapState('attr', ['attr'])
  },
  methods: {
    addAttr() {
      this.$refs.formRef.validate(async (valid) => {
        if (valid) {
          const { category3Id, attrForm } = this
          // 特别注意：请求参数一定要搞清楚，名称一定要一模一样
          await addAttr({
            // attrName: 'xxx',
            // attrValueList: [
            //   {
            //     valueName: 'xxx'
            //   }
            // ],
            ...attrForm,
            categoryId: category3Id,
            categoryLevel: 3
          })
          this.$message.success(`${attrForm.id ? '更新' : '添加'}属性成功`)
          this.cancel() // 返回到AttrList
        }
      })
    },
    delAttrValue(index) {
      // 删除客户端数据
      // 要不要发送请求？此时数据还未嫁到服务器去，不需要从服务器删除
      this.attrForm.attrValueList.splice(index, 1)
    },
    // 进入编辑模式
    async showEdit(row) {
      // 只是让input显示而已，并没有让其聚焦
      /*
        问题：更新时数据是服务器响应的,没有isShowEdit属性
        给对象添加新属性isShowEdit，这个属性不是响应式的（就只能更新数据，不能更新用户界面）
        所以input就不会展示 --> focus报错
        解决：
          1. 在赋值时添加isShowEdit属性
          2. $set
      */
      // row.isShowEdit = true
      this.$set(row, 'isShowEdit', true) // 定义响应式数据
      // 更新是异步的
      await this.$nextTick()
      this.$refs.input.focus()
    },
    // 进入显示模式
    addAttrValue(row, index) {
      // 判断用户是否输入的内容，没有内容要删除整行数据
      if (!row.valueName) {
        this.attrForm.attrValueList.splice(index, 1)
        return
      }
      row.isShowEdit = false
    },
    async showAddAttrValue() {
      // 需求1：点击属性值按钮，添加一行数据显示
      // 解决：只要table渲染的数组有一条数据，就会渲染一行数据 -> 给数组添加一条数据
      // 需求2: 添加的这行数据，属性值名称要显示一个input
      // 解决：在table使用插槽语法渲染input（普通数据用prop，特殊结构用插槽）
      // 需求3: 如何让显示的input聚焦（获取焦点）
      // 解决：input.focus() 让input聚焦 --> ref

      this.attrForm.attrValueList.push({
        isShowEdit: true // 才能进入编辑模式
      })
      // 两种写法
      // this.$nextTick(() => {
      //   this.$refs.input.focus()
      // })

      // vue中更新DOM都是异步更新的（将其他同步代码全部执行完毕，才会执行异步更新）
      await this.$nextTick() // 等DOM元素更新完毕，再触发～
      // 聚焦
      // 报错：TypeError: Cannot read properties of undefined (reading 'focus')
      // 原因：this.$refs.input是undefined
      this.$refs.input.focus()
    },
    cancel() {
      this.$emit('setIsShowAttrList')
    }
  }
}
</script>

<style>
.attr-card {
  margin: 10px;
}
.attr-input {
  width: 200px;
}
.attr-btn {
  margin-top: 10px;
}
</style>
